/**
 * 文件名    ： AES.java
 * 版权       : XX科技有限公司。
 * 描述       : <描述>
 * 修改时间：2019年2月18日
 * 修改内容：<修改内容>
 */
package com.example.demo.application.common.encrypt;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.example.demo.application.common.MyConstants;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class AES {

	public static String rootKey = "";
	private static String encodingFormat = "utf-8";

	/**
	 * <功能> AES_CBC加密 <详细描述>
	 * 
	 * @param sSrc           明文
	 * @param encodingFormat 字符格式 UTF-8
	 * @param secretKey      秘钥
	 * @param ivParameter    矢量 (请使用动态iv)
	 * @return
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidAlgorithmParameterException
	 * @throws InvalidKeyException
	 * @throws UnsupportedEncodingException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws Exception
	 * @see [类、类#方法、类#成员]
	 */
	private final static byte[] encrypt(String sSrc, String secretKey, byte[] ivParameter)
			throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
			IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		byte[] raw = secretKey.getBytes();
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		IvParameterSpec iv = new IvParameterSpec(ivParameter);// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
		byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat));
		return encrypted;
	}

	/**
	 * <功能> AES_CBC加密 <详细描述>
	 * 
	 * @param sSrc           明文
	 * @param encodingFormat 字符格式 UTF-8
	 * @param secretKey      秘钥
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 * @throws Exception
	 * @see [类、类#方法、类#成员]
	 */
	public final static String encrypt(String sSrc, String secretKey)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException,
			IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
		// 动态iv
		byte[] ivParameter = Byte2String.hexStringToByteArray(UUID.randomUUID().toString().replace("-", ""));
		byte[] encr = encrypt(sSrc, secretKey, ivParameter);
		int size = ivParameter.length + encr.length;
		byte[] all = new byte[size];
		// byte 合并
		System.arraycopy(ivParameter, 0, all, 0, ivParameter.length);
		System.arraycopy(encr, 0, all, ivParameter.length, encr.length);

		return new BASE64Encoder().encode(all); // 此处使用base64做转码。
	}

	/**
	 * <功能> AES_CBC解密 <详细描述>
	 * 
	 * @param encryptedArr   密文 使用new BASE64Decoder().decodeBuffer(String) 获得字节数组
	 * @param encodingFormat 字符格式 UTF-8
	 * @param secretKey      秘钥
	 * @param ivParameter    矢量
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidAlgorithmParameterException
	 * @throws InvalidKeyException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws Exception
	 * @see [类、类#方法、类#成员]
	 */
	private final static String decrypt(byte[] encryptedArr, String secretKey, byte[] ivParameter)
			throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
			InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		byte[] raw = secretKey.getBytes(encodingFormat);
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		IvParameterSpec iv = new IvParameterSpec(ivParameter);
		cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
		byte[] original = cipher.doFinal(encryptedArr);
		// 和 getBytes 相反
		String originalString = new String(original, encodingFormat);
		return originalString;
	}

	/**
	 * <功能> AES_CBC解密 <详细描述>
	 * 
	 * @param sSrc           明文
	 * @param encodingFormat 字符格式 UTF-8
	 * @param secretKey      秘钥
	 * @return
	 * @throws IOException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 * @throws Exception
	 * @see [类、类#方法、类#成员]
	 */
	public final static String decrypt(String sSrc, String secretKey)
			throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
			InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		// 取前16位
		byte[] all = new BASE64Decoder().decodeBuffer(sSrc);
		// byte 拆分
		byte[] iv = new byte[16];
		byte[] encryptedArr = new byte[all.length - 16];
		System.arraycopy(all, 0, iv, 0, 16);
		System.arraycopy(all, 16, encryptedArr, 0, all.length - 16);
		return decrypt(encryptedArr, secretKey, iv);
	}
/**
	public static void main(String[] args) throws Exception {
		String iv_OnlyUserRoot = "f9e46c8d2bb4463a";/// root秘钥的 静态IV矢量
		// 生产应用秘钥
		// root秘钥 手写
		// 根据root秘钥 解密获得应用级秘钥：(mysql)、(redis)、(用户模块使用的秘钥)、
		// 用户账号密码的秘钥使用盐值（不可逆）。

		// 已确认 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式，key必须为16位长。

		System.out.println("-----  IV 长度 --------------");
		byte[] ivParameter = Byte2String.hexStringToByteArray(UUID.randomUUID().toString().replace("-", ""));
		System.out.println("16 char            :  " + iv_OnlyUserRoot.getBytes("utf-8").length);
		System.out.println("UUID.randomUUID()  :  " + ivParameter.length);
		System.out.println("---------------------------------------------------------------");

		// 需要加密的字串(分組加密)
		String root_key_1 = "ndE5fVoIGdSyTK6";
		String root_key_last = "AidSFyf7lYT394c6L";
		System.out.println("加密前的字串是：root_key_1:  " + root_key_1 + "      root_key_last:  " + root_key_last);
		// 加密
		String enString_1 = AES.encrypt(root_key_1, MyConstants.ROOT_FATHER_KEY);
		System.out.println("root_key加密后的字串 enString_1 是：" + enString_1);
		String enString_last = AES.encrypt(root_key_last, MyConstants.ROOT_FATHER_KEY);
		System.out.println("root_key加密后的字串 enString_last 是：" + enString_last);
		System.out.println("root_key加密后的字串 是：" + enString_1 + enString_last);
		// 解密
		String deString = AES.decrypt(enString_1, MyConstants.ROOT_FATHER_KEY);
		// 解密
		String deString_last = AES.decrypt(enString_last, MyConstants.ROOT_FATHER_KEY);
		System.out.println("解密后的字串是：" + deString + ";" + deString_last);
		System.out.println("---------------------------------------------------------------");
		String root_key = root_key_1 + root_key_last;
		String mysqlKey1 = AES.encrypt("47c95b226ebf42878", root_key);
		String mysqlKey2 = AES.encrypt("93007c834b194c6", root_key);
		System.out.println("mysql_key1    :    " + AES.decrypt(mysqlKey1, root_key) + "     :    " + mysqlKey1);
		System.out.println("mysql_key2    :    " + AES.decrypt(mysqlKey2, root_key) + "     :    " + mysqlKey2);
		System.out.println("mysql_key    :    " + AES.decrypt(mysqlKey1, root_key) + ";" + AES.decrypt(mysqlKey2, root_key));

		String redisKey = AES.encrypt("chen1991", root_key);
		System.out.println("redis_key   :    " + AES.decrypt(redisKey, root_key) + "       :    " + redisKey);

		System.out.println("用户账号密码的秘钥使用hash盐值 uuid  :  " + UUID.randomUUID().toString().replace("-", ""));
		System.out.println("---------------------------------------------------------------");

		System.out.println(UUID.randomUUID().toString().replace("-", ""));

		// test root_key 是秘钥
		String str = AES.encrypt("f452bd0662584c09b50e3b2065875a6e", root_key);
		System.out.println("  f452bd0662584c09b50e3b2065875a6e 加密后的字串" + str.length() + ":" + str);
		System.out.println(str + "  加密后的字串:" + AES.decrypt(str, root_key));
	}
	*/
}
